//
//  DBUtil.m
//  SC
//
//  Created by nhn on 14-8-6.
//  Copyright (c) 2014年 CNUIC. All rights reserved.
//

#import "DBUtil.h"
#import "FMDB.h"

static FMDatabaseQueue* dbqInstance = NULL;

@implementation DBUtil

+ (NSString*)dbPath
{
    NSString* dbPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/saved.db"];
    return dbPath;
}

+ (void)openDBIfNeed
{
	@synchronized(self) {
		if (!dbqInstance) {
			dbqInstance = [self openDB];
            NSLog(@"opendb");
		}
	}
}

+ (void)closeDBIfNeed
{
	@synchronized(self) {
		if (dbqInstance) {
			[self closeDB:dbqInstance];
			dbqInstance = NULL;
            NSLog(@"closedb");
		}
	}
}

+ (FMDatabaseQueue*)openDB
{
    BOOL firstCreate = false;
	static BOOL firstCall = true;
	
	if (firstCall) {
		firstCall = false;
		
		if (![[NSFileManager defaultManager] fileExistsAtPath:[self dbPath]]) {
			NSString* presetDb = [[NSBundle mainBundle] pathForResource:@"saved" ofType:@"db"];
			if ([[NSFileManager defaultManager]  fileExistsAtPath:presetDb]) {
				[[NSFileManager defaultManager] copyItemAtPath:presetDb toPath:[self dbPath] error:nil];
				firstCreate = false;
				addSkipBackupAttributeToItemAtURL([NSURL fileURLWithPath:[self dbPath]]);
	 		} else {
				firstCreate = true;
			}
		} else {
			firstCreate = false;
		}
	} else {
		firstCreate = false;
	}
	
    //Key_weiwei3141
    NSMutableString* key = [NSMutableString stringWithString:@"Key_wei"];
    [key appendString:@"wei31"];
    [key appendString:@"41"];
    FMDatabaseQueue* q = [[FMDatabaseQueue alloc] initWithPath:[self dbPath] password:key];
    NSLog(@"db path: %@", [self dbPath]);
    
    if (firstCreate) {
    }
    
    return q;
}

+ (void)closeDB:(FMDatabaseQueue*)dbq
{
    [dbq close];
}

+ (NSArray*)getArrayFromResultSet:(FMResultSet*)resultSet
{
    NSMutableArray* retArray = [NSMutableArray array];
    while ([resultSet next]) {
        NSMutableDictionary* dict = [NSMutableDictionary dictionary];
        for (int i=0; i<[resultSet columnCount]; i++) {
            NSString* name = [resultSet columnNameForIndex:i];
            NSObject* obj = [resultSet objectForColumnIndex:i];
            [dict setObject:obj forKey:name];
        }
        [retArray addObject:dict];
    }
    
    return retArray;
}

+ (NSArray *)query:(NSString *)sql, ...
{
    va_list args;
    va_start(args, sql);
    
	NSMutableString* fsql = [NSMutableString stringWithCapacity:[sql length]];
	NSMutableArray* argArray = [NSMutableArray array];
    
	[self extractSQL:sql argumentsList:args intoString:fsql arguments:argArray];
    
    va_end(args);
    
    [self openDBIfNeed];
    __block NSArray* resultArray = nil;
    
    [dbqInstance inDatabase:^(FMDatabase *db) {
        FMResultSet* resultSet = [db executeQuery:fsql withArgumentsInArray:argArray orDictionary:nil orVAList:nil];
        resultArray = [self getArrayFromResultSet:resultSet];
    }];
    
    return resultArray;
}

+ (BOOL)update:(NSString*)sql, ...
{
    va_list args;
    va_start(args, sql);
    
	NSMutableString* fsql = [NSMutableString stringWithCapacity:[sql length]];
	NSMutableArray* argArray = [NSMutableArray array];
	[self extractSQL:sql argumentsList:args intoString:fsql arguments:argArray];
    va_end(args);
    
    [self openDBIfNeed];
    __block BOOL result = false;
    
    [dbqInstance inDatabase:^(FMDatabase *db) {
        result = [db executeUpdate:fsql error:nil withArgumentsInArray:argArray orDictionary:nil orVAList:nil];
    }];
    
    return result;
}

+ (void)openDBWithBlock:(void (^)(FMDatabase *))block
{
	[self openDBIfNeed];
    [dbqInstance inTransaction:^(FMDatabase *db, BOOL *rollback) {
        if (block) {
            block(db);
        }
    }];
}

+ (void)printQuery:(NSString*)sql
{
	NSLog(@"printQuery: <<<< ");
	NSArray* result = [self query:sql];
	if (result && result.count) {
		for (NSDictionary* dict in result) {
			NSLog(@"printQuery: %@", dict);
		}
	} else {
		NSLog(@"printQuery: %@", sql);
	}
	NSLog(@"printQuery: >>>> ");
}

+ (void)extractSQL:(NSString *)sql argumentsList:(va_list)args intoString:(NSMutableString *)cleanedSQL arguments:(NSMutableArray *)arguments {
    
    NSUInteger length = [sql length];
    unichar last = '\0';
    for (NSUInteger i = 0; i < length; ++i) {
        id arg = nil;
        unichar current = [sql characterAtIndex:i];
        unichar add = current;
		bool doNotChangeLast = false;
        if (last == '%') {
            switch (current) {
				// astro add for %%
            	case '%':
					add = '%';
                    last = '\0';
					doNotChangeLast = true;
					break;
                case '@':
                    arg = va_arg(args, id);
                    break;
                case 'c':
                    // warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg has undefined behavior because arguments will be promoted to 'int'
                    arg = [NSString stringWithFormat:@"%c", va_arg(args, int)];
                    break;
                case 's':
                    arg = [NSString stringWithUTF8String:va_arg(args, char*)];
                    break;
                case 'd':
                case 'D':
                case 'i':
                    arg = [NSNumber numberWithInt:va_arg(args, int)];
                    break;
                case 'u':
                case 'U':
                    arg = [NSNumber numberWithUnsignedInt:va_arg(args, unsigned int)];
                    break;
                case 'h':
                    i++;
                    if (i < length && [sql characterAtIndex:i] == 'i') {
                        //  warning: second argument to 'va_arg' is of promotable type 'short'; this va_arg has undefined behavior because arguments will be promoted to 'int'
                        arg = [NSNumber numberWithShort:(short)(va_arg(args, int))];
                    }
                    else if (i < length && [sql characterAtIndex:i] == 'u') {
                        // warning: second argument to 'va_arg' is of promotable type 'unsigned short'; this va_arg has undefined behavior because arguments will be promoted to 'int'
                        arg = [NSNumber numberWithUnsignedShort:(unsigned short)(va_arg(args, uint))];
                    }
                    else {
                        i--;
                    }
                    break;
                case 'q':
                    i++;
                    if (i < length && [sql characterAtIndex:i] == 'i') {
                        arg = [NSNumber numberWithLongLong:va_arg(args, long long)];
                    }
                    else if (i < length && [sql characterAtIndex:i] == 'u') {
                        arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)];
                    }
                    else {
                        i--;
                    }
                    break;
                case 'f':
                    arg = [NSNumber numberWithDouble:va_arg(args, double)];
                    break;
                case 'g':
                    // warning: second argument to 'va_arg' is of promotable type 'float'; this va_arg has undefined behavior because arguments will be promoted to 'double'
                    arg = [NSNumber numberWithFloat:(float)(va_arg(args, double))];
                    break;
                case 'l':
                    i++;
                    if (i < length) {
                        unichar next = [sql characterAtIndex:i];
                        if (next == 'l') {
                            i++;
                            if (i < length && [sql characterAtIndex:i] == 'd') {
                                //%lld
                                arg = [NSNumber numberWithLongLong:va_arg(args, long long)];
                            }
                            else if (i < length && [sql characterAtIndex:i] == 'u') {
                                //%llu
                                arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)];
                            }
                            else {
                                i--;
                            }
                        }
                        else if (next == 'd') {
                            //%ld
                            arg = [NSNumber numberWithLong:va_arg(args, long)];
                        }
                        else if (next == 'u') {
                            //%lu
                            arg = [NSNumber numberWithUnsignedLong:va_arg(args, unsigned long)];
                        }
                        else {
                            i--;
                        }
                    }
                    else {
                        i--;
                    }
                    break;
                default:
                    // something else that we can't interpret. just pass it on through like normal
                    break;
            }
        }
        else if (current == '%') {
            // percent sign; skip this character
            add = '\0';
        }
        
        if (arg != nil) {
            [cleanedSQL appendString:@"?"];
            [arguments addObject:arg];
        }
        else if (add == (unichar)'@' && last == (unichar) '%') {
            [cleanedSQL appendFormat:@"NULL"];
        }
        else if (add != '\0') {
            [cleanedSQL appendFormat:@"%C", add];
        }
		
		if (!doNotChangeLast) {
            last = current;
		}
    }
}

@end
